﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Sanford.Multimedia.Midi;
using Sanford.Multimedia.Midi.UI;
using System.Windows.Forms;
using System.Threading;

namespace AppServidor
{
    class IntrumentoMusical
    {

        public const int Mu = -1;//es mute nota silenciosa 
        public const int Do = 0;
        public const int Do4=1;//Do#
        public const int Re = 2;
        public const int Re4 = 3;//Re#
        public const int Mi = 4;
        public const int Fa = 5;
        public const int Fa4 = 6;//Fa#
        public const int Sol = 7;
        public const int Sol4 = 8;//Sol#
        public const int La = 9;
        public const int La4 = 10;//La#
        public const int Si = 11;

        

        private static Object sinc1=new Object();
        private static Object sinc2 = new Object();
        private static Object sinc3 = new Object();
        private static Object sinc4 = new Object();
        private static Object sinc5 = new Object();
        private static Object sinc6 = new Object();
        private static Object sinc7 = new Object();
        private static Object sinc8 = new Object(); 
        private static Object sinc9 = new Object();
        private static Object sinc10 = new Object();
        private static Object sinc11= new Object();
        private static Object sinc12 = new Object();
        private static Object sinc13 = new Object();
        private static Object sinc14= new Object();
        private static Object sinc15 = new Object();
        private static Object sinc16 = new Object();
        private bool scrolling = false;

        private bool playing = false;

        private bool closing = false;

        private OutputDevice outDevice;

        private int outDeviceID = 0;
        int volumen;
        int canal;
        List<IGraficarNota> graficadores;
        public int Canal
        {
            get { return canal; }
            set { canal = value; }
        }
        int sonido;

        public int Sonido
        {
            get { return sonido; }
            set { sonido = value; }
        }
        int duracion_ms;

        public int Duracion_ms
        {
            get { return duracion_ms; }
            set { duracion_ms = value; }
        }

        public int Volumen
        {
            get { return volumen; }
            set { 
                if(value>=0&&value<=127)
                volumen = value; }
        }

        private float[,] frecuencias_notas_musicales = 
       //--oc:0-----oc:1----oc:2------oc:3-----oc:4---oc:5-----oc:6-------oc:7------oc:8-----|Nota
        { { 16.35F, 32.70F, 065.41F, 130.81F, 261.63F, 523.25F, 1046.50F, 2093.00F, 4186.01F },//Do
          { 17.32F, 34.65F, 069.30F, 138.59F, 277.18F, 554.37F, 1108.73F, 2217.46F,    0.00F },//Do#
          { 18.35F, 36.71F, 073.42F, 146.83F, 293.66F, 587.33F, 1174.66F, 2349.32F,    0.00F },//Re
          { 19.44F, 38.89F, 077.78F, 155.56F, 311.13F, 622.25F, 1244.51F, 2489.02F,    0.00F },//Re#
          { 20.60F, 41.20F, 082.41F, 164.81F, 329.63F, 559.26F, 1318.51F, 2637.02F,    0.00F },//Mi
          { 21.12F, 43.65F, 087.31F, 174.61F, 349.23F, 598.46F, 1396.91F, 2793.83F,    0.00F },//Fa
          { 23.12F, 46.25F, 092.50F, 185.00F, 369.99F, 739.99F, 1479.98F, 2959.96F,    0.00F },//Fa#
          { 24.49F, 49.00F, 098.00F, 196.00F, 392.00F, 783.99F, 1567.98F, 3135.96F,    0.00F },//Sol
          { 25.95F, 51.91F, 103.83F, 207.65F, 415.30F, 830.61F, 1661.22F, 3322.44F,    0.00F },//Sol#
          { 27.50F, 55.00F, 110.00F, 220.00F, 440.00F, 880.00F, 1760.00F, 3520.00F,    0.00F },//La
          { 29.14F, 58.27F, 116.54F, 233.08F, 466.16F, 932.33F, 1864.66F, 3729.31F,    0.00F },//La#
          { 30.87F, 61.74F, 123.47F, 246.94F, 493.88F, 987.77F, 1975.53F, 3951.07F,    0.00F },//Si
         

        };

        int nota;

        public int Nota
        {
            get { return nota; }
            set { nota = value; }
        }
        int octava;

        public int Octava
        {
            get { return octava; }
            set { octava = value; }
        }
        private OutputDeviceDialog outDialog = new OutputDeviceDialog();
        private Sequence sequence1;
        private Sequencer sequencer1;

        private IntrumentoMusical() {
            graficadores = new List<IGraficarNota>();
            init_midi();
        }
       private static IntrumentoMusical instance;

        public static IntrumentoMusical newInstance() {

            if (instance == null) {
                instance = new IntrumentoMusical();
            }
            return instance;
        }

        public void add(IGraficarNota g) {
            graficadores.Add(g);

        }


        private float getFrecuencia(String nota, int octava) {
            return getFrecuencia(Nota_txt_to_num(nota), octava);
        }

        private float getFrecuencia(int notaCod, int octava)
        {
            
            if (notaCod < IntrumentoMusical.Do || notaCod > IntrumentoMusical.Si)
                return 0;
            if (octava < 0 || octava > 8)
                return 0;

            return frecuencias_notas_musicales[notaCod, octava];
        }
        private void init_midi() {
            volumen = 127;
            
            this.sequence1 = new Sanford.Multimedia.Midi.Sequence();
            this.sequencer1 = new Sanford.Multimedia.Midi.Sequencer();
            if (OutputDevice.DeviceCount == 0)
            {
                MessageBox.Show("No MIDI output devices available.", "Error!",
                    MessageBoxButtons.OK, MessageBoxIcon.Stop);

              //  Close();
            }
            else
            {
                try
                {
                    outDevice = new OutputDevice(outDeviceID);

                  //  sequence1.LoadProgressChanged += HandleLoadProgressChanged;
                   // sequence1.LoadCompleted += HandleLoadCompleted;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error!",
                        MessageBoxButtons.OK, MessageBoxIcon.Stop);

                   // Close();
                }
            }
        }


        public int Nota_txt_to_num(String cad) { 
            if(String.IsNullOrWhiteSpace(cad)){
            return -1;
            }
            String nota=cad.ToLower().Trim();

            if ("do".Equals(nota))
                return Do;
            if ("do#".Equals(nota))
                return Do4;
            if ("re".Equals(nota))
                return Re;
            if ("re#".Equals(nota))
                return Re4;
            if ("mi".Equals(nota))
                return Mi;
            if ("fa".Equals(nota))
                return Fa;
            if ("fa#".Equals(nota))
                return Fa4;
            if ("sol".Equals(nota))
                return Sol;
            if ("sol#".Equals(nota))
                return Sol4;
            if ("la".Equals(nota))
                return La;
            if ("la#".Equals(nota))
                return La4;
            if ("si".Equals(nota))
                return Si;
            return -1;
        }

        public bool Reproducir(String nota, int octava, int duracion_ms, int canal) {
            System.Console.WriteLine("Nota: " + nota + " octava: " + octava + " Duracion: " + duracion_ms); 
            return Reproducir(Nota_txt_to_num(nota), octava, duracion_ms, canal);
        }

        public bool Reproducir(int nota, int octava, int duracion_ms, int canal) {


            if (nota < Do || nota > Si) {
                return false;
            }
            if (octava < 0 || octava > 8) {
                return false;
            }
            if (canal < 0 || canal > 15) {
                return false;
            }

            foreach (IGraficarNota g in graficadores)
            {
                g.graficar(getFrecuencia(nota, octava), duracion_ms, canal);
            }
            this.Nota = nota;
            this.octava = octava;
            this.sonido = (octava+1)*12+nota;
            this.canal = canal;
            this.duracion_ms = duracion_ms;

            Thread hilo_reproduccion;
            switch (canal) {
                case 0:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal1));
                    hilo_reproduccion.Start();
                    break;
                case 1:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal2));
                    hilo_reproduccion.Start();
                    break;
                case 2:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal3));
                    hilo_reproduccion.Start();
                    break;
                case 3:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal4));
                    hilo_reproduccion.Start();
                    break;
                case 4:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal5));
                    hilo_reproduccion.Start();
                    break;
                case 5:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal6));
                    hilo_reproduccion.Start();
                    break;
                case 6:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal7));
                    hilo_reproduccion.Start();
                    break;
                case 7:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal8));
                    hilo_reproduccion.Start();
                    break;
                case 8:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal9));
                    hilo_reproduccion.Start();
                    break;
                case 9:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal10));
                    hilo_reproduccion.Start();
                    break;
                case 10:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal11));
                    hilo_reproduccion.Start();
                    break;
                case 11:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal12));
                    hilo_reproduccion.Start();
                    break;
                case 12:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal13));
                    hilo_reproduccion.Start();
                    break;
                case 13:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal14));
                    hilo_reproduccion.Start();
                    break;
                case 14:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal15));
                    hilo_reproduccion.Start();
                    break;
                case 15:
                    hilo_reproduccion = new Thread(new ThreadStart(reproducir_canal16));
                    hilo_reproduccion.Start();
                    break;
            
            }

            return true;
        }

        private void reproducir_()
        {

            int timex = this.duracion_ms / 10;
            for(int t=0;t<timex;t++){
             outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, this.Canal, this.sonido, volumen));
           
            Thread.Sleep(10);
                outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, this.Canal, this.sonido, 0));
            }
                
        }

        private void reproducir_canal1() {
         
           lock (sinc1)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal2()
        {

            lock (sinc2)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal3()
        {

            lock (sinc3)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal4()
        {

            lock (sinc4)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal5()
        {

            lock (sinc5)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal6()
        {

            lock (sinc6)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
              private void reproducir_canal7() {
         
           lock (sinc1)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
              }

           private void reproducir_canal8() {
         
           lock (sinc8)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
          
        private void reproducir_canal9() {
         
           lock (sinc9)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }

        private void reproducir_canal10()
        {

            lock (sinc10)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }

        private void reproducir_canal11()
        {

            lock (sinc11)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal12()
        {

            lock (sinc12)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal13()
        {

            lock (sinc13)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal14()
        {

            lock (sinc14)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal15()
        {

            lock (sinc15)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
        private void reproducir_canal16()
        {

            lock (sinc16)//provoca que el metodo solo sea utilizado por un hilo a la vez.
            {
                reproducir_();
            }
        }
    }
}
